unit DIrr.Texture;

{$I DIrr.inc}

interface

uses
  DIrr.Base, DIrr.Types, DIrr.Dimension2D, DIrr.Color, DIrr.Vector2D,
  DIrr.Rect;

type
  TImageLoader = class abstract(TNamedObject)

  end;

  TImageWriter = class abstract(TNamedObject)

  end;

  TImage = class abstract(TNamedObject)
  public
    //! Lock function. Use this to get a pointer to the image data.
    (** After you don't need the pointer anymore, you must call unlock().
    \return Pointer to the image data. What type of data is pointed to
    depends on the color format of the image. For example if the color
    format is ECF_A8R8G8B8, it is of u32. Be sure to call unlock() after
    you don't need the pointer any more. *)
    function Lock: Pointer; virtual; stdcall; abstract;

    //! Unlock function.
    (** Should be called after the pointer received by lock() is not
    needed anymore. *)
    procedure Unlock; virtual; stdcall; abstract;

    //! Returns width and height of image data.
    function GetDimension: PDimension2DU; virtual; stdcall; abstract;

    //! Returns bits per pixel.
    function GetBitsPerPixel: U32; virtual; stdcall; abstract;

    //! Returns bytes per pixel
    function GetBytesPerPixel: U32; virtual; stdcall; abstract;

    //! Returns image data size in bytes
    function GetImageDataSizeInBytes: U32; virtual; stdcall; abstract;

    //! Returns image data size in pixels
    function GetImageDataSizeInPixels: U32; virtual; stdcall; abstract;

    //! Returns a pixel
    function GetPixel(x, y: U32): TIrrColor; virtual; stdcall; abstract;
    function GetPixel32(x, y: U32): U32; virtual; stdcall; abstract;

    //! Sets a pixel
    procedure SetPixel(x, y: U32; const Color: TIrrColor;
      Blend: Boolean = False); virtual; stdcall; abstract;

    //! Returns the color format
    function GetColorFormat: ECOLOR_FORMAT; virtual; stdcall; abstract;

    //! Returns mask for red value of a pixel
    function GetRedMask: U32; virtual; stdcall; abstract;

    //! Returns mask for green value of a pixel
    function GetGreenMask: U32; virtual; stdcall; abstract;

    //! Returns mask for blue value of a pixel
    function GetBlueMask: U32; virtual; stdcall; abstract;

    //! Returns mask for alpha value of a pixel
    function GetAlphaMask: U32; virtual; stdcall; abstract;

    //! Returns pitch of image
    function GetPitch: U32; virtual; stdcall; abstract;

    //! Copies the image into the target, scaling the image to fit
    procedure CopyToScaling(Target: Pointer; Width, Height: U32;
      Fmt: ECOLOR_FORMAT = ECF_A8R8G8B8; Pitch: U32 = 0); overload; virtual; stdcall; abstract;

    //! Copies the image into the target, scaling the image to fit
    procedure CopyToScaling(Target: TImage); overload; virtual; stdcall; abstract;

    //! copies this surface into another
    procedure CopyTo(Target: TImage; const APos: TPosition2D); overload; virtual; stdcall; abstract;

    //! copies this surface into another
    procedure CopyTo(Target: TImage; const APos: TPosition2D; const SourceRect: TIrrRect;
      ClipRectPtr: PIrrRect = nil); overload; virtual; stdcall; abstract;

    //! copies this surface into another, using the alpha mask and cliprect and a color to add with
    procedure CopyToWithAlpha(Target: TImage; const APos: TPosition2D; const SourceRect: TIrrRect;
      const Color: TIrrColor; ClipRectPtr: PIrrRect = nil); virtual; stdcall; abstract;

    //! copies this surface into another, scaling it to fit, appyling a box filter
    procedure CopyToScalingBoxFilter(Target: TImage; Bias: S32 = 0; Blend: Boolean = False); virtual; stdcall; abstract;

    //! fills the surface with given color
    procedure Fill(const Color: TIrrColor); virtual; stdcall; abstract;
  public
    class function GetBitsPerPixelFromFormat(const Fmt: ECOLOR_FORMAT): U32; static;
    class function IsRenderTargetOnlyFormat(const Fmt: ECOLOR_FORMAT): Boolean; static;
  public
    procedure CopyTo(Target: TImage); overload; inline; { Position = (0, 0) }
  end;

  TTexture = class abstract(TNamedObject)
  protected
    FNamedPath: TIrrAnsiString;
  public
    constructor Create(TheName: TIrrAnsiString);
  public
    //! Lock function.
    (** Locks the Texture and returns a pointer to access the
    pixels. After lock() has been called and all operations on the pixels
    are done, you must call unlock().
    Locks are not accumulating, hence one unlock will do for an arbitrary
    number of previous locks. You should avoid locking different levels without
    unlocking inbetween, though, because only the last level locked will be
    unlocked.
    The size of the i-th mipmap level is defined as max(getSize().Width>>i,1)
    and max(getSize().Height>>i,1)
    \param mode Specifies what kind of changes to the locked texture are
    allowed. Unspecified behavior will arise if texture is written in read
    only mode or read from in write only mode.
    Support for this feature depends on the driver, so don't rely on the
    texture being write-protected when locking with read-only, etc.
    \param mipmapLevel Number of the mipmapLevel to lock. 0 is main texture.
    Non-existing levels will silently fail and return 0.
    \return Returns a pointer to the pixel data. The format of the pixel can
    be determined by using getColorFormat(). 0 is returned, if
    the texture cannot be locked. *)
    function Lock(mode: E_TEXTURE_LOCK_MODE = ETLM_READ_WRITE; MipMapLevel: U32 = 0): Pointer; virtual; stdcall; abstract;

    //! Unlock function. Must be called after a lock() to the texture.
    (** One should avoid to call unlock more than once before another lock.
    The last locked mip level will be unlocked. *)
    procedure Unlock; virtual; stdcall; abstract;

    //! Get original size of the texture.
    (** The texture is usually scaled, if it was created with an unoptimal
    size. For example if the size was not a power of two. This method
    returns the size of the texture it had before it was scaled. Can be
    useful when drawing 2d images on the screen, which should have the
    exact size of the original texture. Use ITexture::getSize() if you want
    to know the real size it has now stored in the system.
    \return The original size of the texture. *)
    function GetOriginalSize: PDimension2DU; virtual; stdcall; abstract;

    //! Get dimension (=size) of the texture.
    (** \return The size of the texture. *)
    function GetSize: PDimension2DU; virtual; stdcall; abstract;

    //! Get driver type of texture.
    (** This is the driver, which created the texture. This method is used
    internally by the video devices, to check, if they may use a texture
    because textures may be incompatible between different devices.
    \return Driver type of texture. *)
    function GetDriverType: E_DRIVER_TYPE; virtual; stdcall; abstract;

    //! Get the color format of texture.
    (** \return The color format of texture. *)
    function GetColorFormat: ECOLOR_FORMAT; virtual; stdcall; abstract;

    //! Get pitch of the main texture (in bytes).
    (** The pitch is the amount of bytes used for a row of pixels in a
    texture.
    \return Pitch of texture in bytes. *)
    function GetPitch: U32; virtual; stdcall; abstract;

    //! Check whether the texture has MipMaps
    (** \return True if texture has MipMaps, else false. *)
    function HasMipMaps: Boolean; virtual; stdcall;

    //! Returns if the texture has an alpha channel
    function HasAlpha: Boolean; virtual; stdcall;

    //! Regenerates the mip map levels of the texture.
    (** Required after modifying the texture, usually after calling unlock().
    \param mipmapData Optional parameter to pass in image data which will be
    used instead of the previously stored or automatically generated mipmap
    data. The data has to be a continuous pixel data for all mipmaps until
    1x1 pixel. Each mipmap has to be half the width and height of the previous
    level. At least one pixel will be always kept.*)
    procedure RegenerateMipMapLevels(MipMapData: Pointer = nil); virtual; stdcall; abstract;

    //! Check whether the texture is a render target
    (** Render targets can be set as such in the video driver, in order to
    render a scene into the texture. Once unbound as render target, they can
    be used just as usual textures again.
    \return True if this is a render target, otherwise false. *)
    function IsRenderTarget: Boolean; virtual; stdcall;

    function GetFullName: PAnsiChar; virtual; stdcall;

    //! Get name of texture (in most cases this is the filename)
    function GetName: PIrrAnsiString; virtual; stdcall;
  end;

  SRenderTarget = record
  public
    RenderTexture: TTexture;
    TargetType: E_RENDER_TARGET;
    ColorMask: E_COLOR_PLANE;
  private
    FBlendFactor: Word;
    function GetBlendFuncDst: E_BLEND_FACTOR; inline;
    function GetBlendFuncSrc: E_BLEND_FACTOR; inline;
    procedure SetBlendFuncDst(const Value: E_BLEND_FACTOR); inline;
    procedure SetBlendFuncSrc(const Value: E_BLEND_FACTOR); inline;
  public
    BlendOp: E_BLEND_OPERATION;
  public
    property BlendFuncSrc: E_BLEND_FACTOR read GetBlendFuncSrc write SetBlendFuncSrc;
    property BlendFuncDst: E_BLEND_FACTOR read GetBlendFuncDst write SetBlendFuncDst;
    //property BlendOp: E_BLEND_OPERATION read GetBlendOp write SetBlendOp;
  end;

implementation

{ SRenderTarget }

function SRenderTarget.GetBlendFuncDst: E_BLEND_FACTOR;
begin
  Result := E_BLEND_FACTOR((FBlendFactor and $F0) shr 4);
end;

function SRenderTarget.GetBlendFuncSrc: E_BLEND_FACTOR;
begin
  Result := E_BLEND_FACTOR(FBlendFactor and $F);
end;

procedure SRenderTarget.SetBlendFuncDst(const Value: E_BLEND_FACTOR);
begin
  FBlendFactor := ((Word(Value) and $F) shl 4) + (FBlendFactor and $F);
end;

procedure SRenderTarget.SetBlendFuncSrc(const Value: E_BLEND_FACTOR);
begin
  FBlendFactor := (FBlendFactor and $F0) + (Word(Value) and $0F);
end;

{ TImage }

procedure TImage.CopyTo(Target: TImage);
begin
  CopyTo(Target, Position2D(0, 0));
end;

class function TImage.GetBitsPerPixelFromFormat(const Fmt: ECOLOR_FORMAT): U32;
begin
  case Fmt of
    ECF_A1R5G5B5: Result := 16;
    ECF_R5G6B5: Result := 16;
    ECF_R8G8B8: Result := 24;
    ECF_A8R8G8B8: Result := 32;
    ECF_R16F: Result := 16;
    ECF_G16R16F: Result := 32;
    ECF_A16B16G16R16F: Result := 64;
    ECF_R32F: Result := 32;
    ECF_G32R32F: Result := 64;
    ECF_A32B32G32R32F: Result := 128;
    else
      Result := 0;
  end;
end;

class function TImage.IsRenderTargetOnlyFormat(
  const Fmt: ECOLOR_FORMAT): Boolean;
begin
  Result := not (Fmt in [ECF_A1R5G5B5, ECF_R5G6B5, ECF_R8G8B8, ECF_A8R8G8B8]);
end;

{ TTexture }

constructor TTexture.Create(TheName: TIrrAnsiString);
begin
  FNamedPath := TheName;
end;

function TTexture.GetFullName: PAnsiChar;
begin
  Result := FNamedPath.CharPtr;
end;

function TTexture.GetName: PIrrAnsiString;
begin
  Result := @FNamedPath;
end;

function TTexture.HasAlpha: Boolean;
begin
  Result := False;
end;

function TTexture.HasMipMaps: Boolean;
var
  ColorFormat: ECOLOR_FORMAT;
begin
  ColorFormat := GetColorFormat;
  Result := ColorFormat in [ECF_A8R8G8B8, ECF_A1R5G5B5];
end;

function TTexture.IsRenderTarget: Boolean;
begin
  Result := False;
end;

end.
